/*
  名称：基于MQTT的万能AT指令（名称来源于本人名字故取名为YJ指令）
  本程序参照WZ-mqtt源代码编写，主要功能是通过RS232端口硬件和发射机遥控指令协议将发射机增加智能物联网上云功能（参见
  《杭淳CDR发射机远程监控协议1801》  的遥控协议），通过该程序可以实现功能：1.上电后，通过智能联网自动连接本地WIFI，
  并登录EMQ服务器(服务器ip：182.168.3.2，port=1883可选定其他服务器地址)，登陆成功后，指示灯每5秒闪亮一次进入正常
  工作状态。2。每5秒发送一次读取下位机运行参数指令（7E E7 01 07 00 11 01 ED A5 C6 E8 AA ），下位机返回38byte相
  应回复（7e e7 01 07 19 11 01 00 03 ef 00 64 25 00 5b 00 ee 01 e1 00 25 00 a9 00 9f 00 95 ff cf 00 01
  00 00 e1 b2 7c da aa），模块按照通讯协议解析出发射机运行参数 并按照JSON格式（相应的语句为： sprintf(msg_buf,
  "{\"PwrFwd\":%d,\"PwrRfl\":%d,\"Temp\":%d,\"Vadj\":%d,\"Vdrv\":%d,\"Vamp\":%d,\"\Idrv\":%d,\"I1\":%d,
  \"I2\":%d,\"I3\":%d,\"InLevel\":%d,\"Status1\":%d,\"\Status2\":%d}",PwrFwd,PwrRfl,Temp,Vadj,Vamp,
  Vdrv,Idrv,I1,I2,I3,InLevel,Status1,Status2);//构建/pub/8266报文）写好数据并发送到EMQ服务器。3，模块订阅发射
  机控制指令并负责向下位机发出相应的指令（开机（7E E7 01 07 00 20 01 2C 35 37 0D AA）、关机（7E E7 01 07 00 21
  01 F0 58 AD BA AA）、设置参数（7E E7 01 07 00 10 01 31 C8 5C 5F AA）、锁定功率等（7E E7 01 07 00 22 01 02
  00 30 DD 81 DB AA））。4，为保证模块稳定工作，模块还具备看门狗、断线重连和智能配网等功能。

2021年11月23日升级版本，版本号：yj_mqtt_v3.501版本，进一步稳定拟定串口接收数据
串口发送以下有干扰的数据，模块可以准确分离出帧数据，滤除干扰数据，可以实现一个模块链接多个下位机，实现一对多稳定通讯
ef 00 64 25 00 5b 00 ee7e e7 01 07 19 11 01 00 03 ef 00 64 25 00 5b 00 ee 01 e1 00 25 00 a9 00 9f 00 95 ff cf 00 01 00 00 e1 b2 7c da aa ef 00 64 25 00 5b 00 ee


2021年11月19日完成CRC32-MPEG2校验功能功能，版本号：yj_mqtt_v3.01版本

2021年11月优化yj_mqtt_v2.01版本，已基本具备上述功能（crc校验待完善）。
  
 yj_mqtt_v3.01版本EMQ服务器等相关参数约定：
  server：ip或者域名=192.168.3.2
  port=1883：端口（可不添加默认1883）
  mqtt_id = "YJ001"; //客户端ID
  mqtt_user = "Admin"; //用户名
  mqtt_password = "123456"; //设备密码
  topic="/pub/8266"：发布的主题
  sub_topic="/sub/8266"：订阅的主题
  message：发布的消息
  
 
  
  2021年10月优化yj_mqtt_v1.01版本
  开始增加开机、关机、设定参数和锁定功率等几个指令
  订阅主题获取到on\off\set\lock字符串时，模块向串口发出相应的指令代码，此办法比较简陋，有待于增加安全措施

  2020年11月优化为YJ——ver2.0.0版本
  读取下位机运行参数（16进制），转化成原来的10进制json格式以后，发布到EMQ服务器



  
  优化指示灯，采用定时器实现。
  
  2021年9月优化4.01版本
  开始考虑增加每隔6000毫秒向串口发送一次读取下位机信息的指令。

  2020年2月优化4.0版本
  优化指示灯，采用定时器实现。

  版本：V3.8
  2019年7月24日 清除判网逻辑
  2019年6月22日00:15更新看门狗逻辑(看门狗默认开启)，断网处理逻辑，修复断网卡死，修复无限循环配网的问题，基本实现稳定运行。 WZ指令更换WIFI待优化。
  2019年4月18日更新sub_topic字段 用于用户自定义Topic
  更新：2019年4月11日修复onenet大于3个数据点内存溢出问题
       修复数据反馈异常，清除多余打印数据。
  特点：简单到只需要一行代码就可以将数据点推送至MQTT服务器
  可用于个人EMQ服务器，或者树莓派搭建的局域网服务器，已支持onenet等商用平台
  更新日志：
  2019年2月18日午夜更新V3.0版本
  支持onenet平台
  2019年2月13日晚上制作V2.0版本
  添加mqtt_user验证
  添加mqtt_passward验证
  2019年2月13日上午制作V1.0版本
  实现基本通讯
  2019年初有初步想法
  网站：http://wenzheng.club
  Github:https://github.com/az666
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <EEPROM.h>
#include <PubSubClient.h>
#include <Ticker.h>
WiFiClient espClient;
PubSubClient client(espClient);
Ticker flipper;
const byte Head_H=0x7E;//相当于#define Head_h 0x7E定义帧头的高字节
const byte Head_L=0xE7;//代表帧头的低字节
const byte Addr=0x01;//代表下位机设备的地址
const byte Dev=0x07;//设备类型
const byte Len=0x19;//DATA数据字节的长度
const byte BUF_SIZE=38;//串口接收缓冲区的长度
char buf[BUF_SIZE]="";//定义串口接收缓存区
int buf_size=0;
int flipper_count = 0;
struct CONFIG {  //结构体存放账号密码主题和消息
  char ssid[32];
  char password[32];
  char server[32];
  char topic[32];
  char sub_topic[32] ;
  char message[200];
  char onenet_topic_msg[200];
};
String payload = "";
String inputString = "";//串口读入数据

String mqtt_user;
int port;
String mqtt_password;
String mqtt_id;
boolean stringComplete = false;
long lastMsg = 0;
long lastMsg1 = 0;
int led_flag = 0;
int flip_flag = 0;
int wifi_err_time = 0;

char  c[] = "";
int server_flag = 0;
char msgJson[200];  //存储json数据
char debug_buf[200];  //打印调试数据
char msg_buf[200];//发送给mqtt服务器的消息数据

unsigned char data[12] = {0x7E , 0xE7, 0x01, 0x07, 0x00, 0x11, 0x01, 0xED, 0xA5, 0xC6, 0xE8, 0xAA}; //定义数组,读运行参数指令
unsigned char com_on[12] = {0x7E , 0xE7, 0x01, 0x07, 0x00, 0x20, 0x01, 0x2C, 0x35, 0x37, 0x0D, 0xAA}; //定义开机指令数组
unsigned char com_off[12] = {0x7E , 0xE7, 0x01, 0x07, 0x00, 0x21, 0x01, 0xF0, 0x58, 0xAD, 0xBA, 0xAA}; //定义关机指令数组
unsigned char com_set[12] = {0x7E , 0xE7, 0x01, 0x07, 0x00, 0x10, 0x01, 0x31, 0xC8, 0x5C, 0x5F, 0xAA}; //定义设置指令数组
unsigned char p_lock[14] = {0x7E , 0xE7, 0x01, 0x07,0x02,0x22, 0x01, 0x02, 0x00, 0x30, 0xDD, 0x81, 0xDB, 0xAA}; //定义锁定功率指令数组
//以下是声明一个结构体类型RxFrame，用于存放下位机返回的发射机运行参数：PwrFwd、PwrRfl、Temp、Vadj、Vdrv、 Vamp、Idrv、I1、I2、I3、InLevel、Status1、Status2
struct RxFrame
{
    byte Head_h;
    byte Head_l;
    byte Addr;
    byte Dev;
    byte Len; 
    byte Cmd;
    byte CmdAux;
    byte Status;   
    unsigned int PwrFwd;//0-65535
    unsigned int PwrRfl;//0-65535
    char Temp;          //-128--127
    unsigned int Vadj;//0--65535
    unsigned int Vdrv;//0--65535
    unsigned int Vamp;//0--65535
    unsigned int Idrv;//0--65535
    unsigned int I1;  //0--65535
    unsigned int I2;  //0--65535
    unsigned int I3;  //0--65535
    short InLevel;    //-32768~32767
    unsigned int Status1;//0-65535
    unsigned int Status2;//0-65535
    unsigned long Crc;//四个字节的CRC校验码
    byte Tail;


//    unsigned char header1;  //0--255 
//    unsigned char header2; 
//    short deviceId;  //-32768~32767
//    float voltage;    //float类型数据占有4字节长度，范围-3.4028235E+38~3.4028235E+38只有6~7位小数精度
//    unsigned char check;//0--255
};

struct RxFrame work_data;//存放下位机的运行参数
unsigned short json_len = 0;

//*************************************************************//
void setup() {
  Serial.begin(115200);//用于通讯的串口1
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
  flipper.attach(1, flip); //100 ms 定时器
  setup_wifi();
//  Serial.print("wifi_OK\r\n");
//  Serial.print("please connect the server!\r\n");
  inputString.reserve(1024);
}
void loop() {
  Mqtt_Task();
  //Serial.print("Mqtt_Task  ok!\r\n");
  Serial_Task();
  //Serial.print("Serial_Task  ok!\r\n");
  Dog_Task();
  //Serial.print("Dog_Task  ok!\r\n");

}
/*********************************定时器回调**************************************/
void flip() {    //定时器回调
  digitalWrite(2, !digitalRead(2));
  switch (flip_flag) //判断标志位
  {
    case 0: //正常工作状态

      break;
    case 1: //发消息状态 快闪5次 恢复正常
      flipper_count++;
      if (flipper_count == 8)
      {
        flipper.attach(1, flip); //恢复工作状态
        flipper_count = 0;
      }
      break;
    case 2: //断网重连状态 一直闪
      //可以加更多逻辑
      break;
    case 3: //wifi连接超时开始配网
      //可以加更多逻辑
      break;
     case 4: //CRC校验出错
      
      Serial.print("CRC Data Error!!!");
      break;
      
    default:
      //可以加更多逻辑
      break;
  }
}
/*********************************看门狗与自检**************************************/
void Dog_Task()
{
  long now_time = millis();

  if (now_time - lastMsg > 2000) {
    lastMsg = now_time;
    ESP.wdtFeed(); //喂狗
    if (!WiFi.isConnected()) //wifi异常
    {
      wifi_err_time += 1;
      if (wifi_err_time > 10)
      {
        if (!WiFi.isConnected()) //wifi异常
          ESP.restart();
      }
    } else
    {
      wifi_err_time = 0;
    }
  }
  if (digitalRead(0) == 0) //拉低主动进入配网模式
  {
    smartConfig();  //微信智能配网
  }

}
/*********************************消息回调函数处理订阅消息控制发射机**************************************/
void callback(char* topic, byte* payload, unsigned int length)  //接收消息
{
//  Serial.print("COMMAND:[");
//  Serial.print(topic);
//  Serial.print("]\r\n");//串口打印输出收到订阅的Topic
  String message="";//清空message
    for (int i = 0; i < length; i++) {//给message赋值为payload。
        message = message + (char) payload[i];  // convert *byte to string
    }
    //以下是执行topic的相应动作
    //Serial.println(message);
    //Serial.print("\r\n");
    if (message == "on")  {Serial.write(com_on, 12); }//发送开机参数指令com_on
    if (message == "off") {Serial.write(com_off, 12); }//发送关机指令com_off
    if (message == "set") {Serial.write(com_set, 12); }//发送设置参数com_set
    if (message == "lock") {Serial.write(p_lock, 14); }//发送锁定功率512w指令p_lock
   
   //Serial.print("\r\n");
}
String macToStr(const uint8_t* mac) //MAC地址码转换成字符串的函数
{
  String result;
  for (int i = 0; i < 6; ++i) {
    result += String(mac[i], 16);
    if (i < 5)
      result += ':';
  }
  return result;
}
/*********************************连接wifi**************************************/
int setup_wifi() {

 // if（buf.ssid==null）{
  EEPROM.begin(512);
  CONFIG buf;
  EEPROM.get<CONFIG>(0, buf);
  delay(1000);
  Serial.println(buf.ssid);
  Serial.println(buf.password);
  //EEPROM.commit();
  delay(1000);
  //}
  WiFi.begin(buf.ssid, buf.password);
  long lastMsg = millis();
  while (WiFi.status() != WL_CONNECTED) {
    //正在连接wifi
    delay(500);
    //Serial.print(".");
    long now = millis();
    if (now - lastMsg > 10000) {
      //wifi 连接超时
      smartConfig();  //微信智能配网
      break;
    }
  }


}
/*********************************服务器连接**************************************/
void reconnect() {
  int connected_try_time = 0;
  while (!client.connected()) {
    flip_flag = 2; //切换为重连状态
    connected_try_time++;
    if (connected_try_time > 10) {
      connected_try_time = 0;
      ESP.restart();
    }
    CONFIG buf;
//          mqtt_id = "YJ001"; //客户端ID
//          mqtt_user = "Admin"; //用户名
//          mqtt_password = "123456"; //设备密码
//          strcpy(buf.topic,"/pub/esp8266"); //发布的主题
    
    String clientName;
    String sub_topic001= "/sub/8266"; //订阅的主题
//    clientName += "esp8266-";
//    uint8_t mac[6];
//    WiFi.macAddress(mac);
//    clientName += macToStr(mac);
//    clientName += "-";
//    clientName += String(micros() & 0xff, 16);
//    if (mqtt_id == "") mqtt_id = clientName; //判断是否要添加ID
//    if  (client.connect((char*)mqtt_id.c_str(), (char*)mqtt_user.c_str(), (char*)mqtt_password.c_str()))  {  //ID与账号和密码
       if  (client.connect("YJ000", "ADMIN", "12345678"))  {  //ID与账号和密码
    
      Serial.println("connected_OK");
      flip_flag = 1; //切换为正常状态
      client.subscribe(sub_topic001.c_str(), 1); //为保证主题不受冲突 (const char*) 
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());  //设备重连
      Serial.print(",try time=");
      Serial.print(connected_try_time);
      Serial.println(" try_to_connected");
    }
  }
}
/***********************CRC32函数几种算法 ，两个入口参数：buf为数据数组，len是数组的长度**********************/
const unsigned int crc32_mpeg2_table[256] = {//查表法求得校验标准CRC32_MPEG2的编码表
    0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 
    0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 
    0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, 
    0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, 
    0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 
    0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, 
    0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 
    0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 
    0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, 
    0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 
    0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 
    0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 
    0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, 
    0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 
    0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 
    0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, 
    0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 
    0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, 
    0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, 
    0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 
    0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, 
    0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 
    0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 
    0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, 
    0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 
    0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 
    0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 
    0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 
    0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 
    0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 
    0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, 
    0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4, 
};
unsigned int do_crc_table(unsigned char *ptr, int len)//查表法计算CRC32_mpeg2结果函数（校验标准CRC32_MPEG2）
{
    unsigned int crc = 0xFFFFFFFF;
    
    while(len--) 
    {
        crc = (crc << 8) ^ crc32_mpeg2_table[(crc >> 24 ^ *ptr++) & 0xff];
    }
    
    return crc;
}
unsigned int do_crc(unsigned char *ptr, int len)//直接计算CRC32_mpeg2标准的结果函数
{
    unsigned int i;
    unsigned int crc = 0xFFFFFFFF;
    
    while(len--)
    {
        crc ^= (unsigned int)(*ptr++) << 24;
        for (i = 0; i < 8; ++i)
        {
            if (crc & 0x80000000)
                crc = (crc << 1) ^ 0x04C11DB7;
            else
                crc <<= 1;
        }
    }
    return crc;


//const unsigned int  CRC32_table[] = {  //校验标准CRC32的编码表 
//    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,   
//    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,   
//    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,   
//    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,   
//    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,   
//    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,   
//    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,   
//    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,   
//    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,   
//    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,   
//    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,   
//    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,   
//    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,   
//    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,   
//    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,   
//    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,   
//    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,   
//    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,   
//    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,   
//    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,   
//    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,   
//    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,   
//    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,   
//    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,   
//    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,   
//    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,   
//    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,   
//    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,   
//    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,   
//    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,   
//    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,   
//    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,   
//};
//
//
//
//unsigned int  getCRC(unsigned char* buf, int nLength)//校验标准CRC32,入口参数buf为数据数组，nLength是数组的长度
//{
//    if (nLength < 1)
//        return 0xffffffff;
//
//    unsigned int crc = 0xFFFFFFFF;
//
//    for (int  i = 0; i < nLength; i++)
//    {
//        Serial.print(buf[i],HEX);// 列出要检验的数据
//          
//        crc = CRC32_table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); 
//         
//    }
//
//    crc = crc ^ 0xFFFFFFFF;//校验标准CRC32_MPEG2要求结果要与0xFFFFFFFF进行异或运算
//
//    return crc;  
//}
//
//uint32_t crc_compute_crc32_ext( uint8_t *buf, uint32_t buf_len)//另一种求得crc32的方法，入口参数buf为数据数组，buf_len是数组的长度。
//{
//    uint32_t CRC32_data =0xFFFFFFFF;
// 
//    for (uint32_t i = 0; i < buf_len; i++)
//    {
//        uint32_t t = (CRC32_data ^ buf[i]) & 0xFF;
//        CRC32_data = ((CRC32_data >> 8) & 0xFFFFFF) ^ CRC32_table[t];
//    }
//    return (uint32_t)(CRC32_data )^ 0xFFFFFFFF;
//}
}

/**********************CRC32函数结束****************************************/
/*********************************串口任务*KLJ*************************************/
void Serial_Task() {
  long now_time1 = millis();
     if (client.connected()&&(WiFi.status() == WL_CONNECTED) ) { //如果服务器在线且WIFI正常时，发送要读取设备参数指令：
       if (now_time1 - lastMsg1 > 6000) {    //每间隔6000毫秒发送一次读取运行参数指令
        lastMsg1 = now_time1;
        Serial.write(data, 12); //发送读取设备参数指令data，获取设备运行参数
        //Serial.print("Serial write command ok!!!\r\n");
      }
   }

  while (Serial.available()) {  //串口接收缓冲区有数据输入事件发生
        buf[buf_size++] = (byte)Serial.read();//读取一个字节数据 
        Serial.print(buf[buf_size-1],HEX);//打印出串口数据
        if  (buf_size >= 2)//接收到2个以上字节的数据
        {
             if (buf[0] == Head_H && buf[1] == Head_L) //接收到帧头
             {
                 //Serial.print("get data -->Head !"); //用HEX格式显示串口读取到的数据
                 if (buf[buf_size-1] == 0xaa ){   //接收到帧尾
                    //此处为数据包处理逻辑
                    Serial.print("get data --> 0xaa!"); //用HEX格式显示串口读取到的数据
                    Serial.print(buf_size,HEX);
                    //Serial.println(buf);//打印出串口数据
                    Serial.print("\n");
                    huanyuan(buf,buf_size);
                    memcpy(&work_data,&buf,buf_size);//将串口接收到的数据串复制给结构体work_data，一共有38个byte长度，包扩25字节的运行参数，帧头帧尾校验及其他12字节)
                    buf_size = 0;
                    memset(buf,0,BUF_SIZE);
                }  
             }
             else
             {
                 buf_size  =  0;
                 memset(buf,0,BUF_SIZE);
             }
        }      
        if(buf_size  >=  BUF_SIZE)
        {
             buf_size = 0;
             memset(buf,0,BUF_SIZE);
        }
     }
   }            
    int huanyuan( char* inputString,int len)//数据打印、还原的函数，入口参数数组inputString：运行参数的25个字节外加帧头命令校验等。
            { 
 /*       
        if (inChar==Head_H) {//找到帧头标识符，判断读入的当前字节是否是0xaa或者'\n'，可以换成其他关键字0xaa。
          inputString += inChar;    //把读取到的一个字节数据存储到inputString数组中
        */    
  //      if( inputString[1]==Head_L&& inputString[2]==Addr&& inputString[3]==Dev&&inputString[37]==0xaa){//判断数据帧的帧头、设备地址和设备类型和帧尾标志
              stringComplete = true;//接收完成标志位置位
              flip_flag = 1; //切换消息状态
              flipper.attach(2, flip);//2秒闪一次
              Serial.println("Serial read data ok!!!");  //此处表示收到完整的数据，可以添加向串口发送收到数据或者控制指令
              int i;
              //unsigned char crc_buf1[]={0x11,0x01,0x00,0x03,0xEF,0x00,0x64,0x25,0x00,0x5B,0x00,0xEE,0x01,0xe1,0x00,0x25,0x00,0xA9,0x00,0x9f,0x00,0x95,0xFF,0xCF,0x00,0x01,0x00,0x00};
              unsigned char crc_buf[]="";
              int data_len=len-10;
              for(i=0;i<data_len;i++){ //取得需要CRC计算的数据段
                crc_buf[i]=inputString[i+5];
                 Serial.print(crc_buf[i],HEX);
                 Serial.print(";");
               }
    
             //  Serial.print("\n");
              unsigned int  crc0,crc1;
              unsigned long crc2;//串口读取的CRC结果
              crc0=do_crc_table(crc_buf, data_len); //查表法求得crc32-MPEG校验值
              crc1=do_crc(crc_buf, data_len);       //直算法求得crc32-MPEG校验值
              crc2=(inputString[data_len+5]<<24)|(inputString[data_len+6]<<16)|(inputString[data_len+7]<<8)|inputString[data_len+8];
              
              //crc=crc_compute_crc32_ext(crc_buf, 28);//查表法求得CRC32校验值
              
           
              Serial.print(crc0,HEX);//打印输出crc0         
              Serial.print(crc1,HEX);//打印输出crc1        
              Serial.print(crc2,HEX);//打印输出crc 
              Serial.print("\n");
              if(crc1!=crc2) {         //数据段校验出错
                stringComplete = false;//接收完成标志位重新置位为false
                inputString="";        //清空有错误的数据
                Serial.print("\n");
                flip_flag = 4; //切换消息状态,CRC校验出错
                }
    //          }
    //          else{
            
                   
            unsigned int PwrFwd=(inputString[8]<<8)|inputString[9];
            unsigned int PwrRfl=(inputString[10]<<8)|inputString[11];
            char Temp =inputString[12];
            unsigned int Vadj=(inputString[13]<<8)|inputString[14];
            unsigned int Vdrv=(inputString[15]<<8)|inputString[16];
            unsigned int Vamp=(inputString[17]<<8)|inputString[18];
            unsigned int Idrv=(inputString[19]<<8)|inputString[20];
            unsigned int I1=(inputString[21]<<8)|inputString[22];
            unsigned int I2=(inputString[23]<<8)|inputString[24];
            unsigned int I3=(inputString[25]<<8)|inputString[26];
            short InLevel=(inputString[27]<<8)|inputString[28];
            unsigned int Status1=(inputString[29]<<8)|inputString[30];
            unsigned int Status2=(inputString[31]<<8)|inputString[32];
 //以下是还原数据的串口输出操作:             
            Serial.printf("Head=%X%X-->%d\n",inputString[0],inputString[1],(inputString[0]<<8)|inputString[1]); 
            Serial.printf("Addr=%X-->%d\n",inputString[2],inputString[2]);
            Serial.printf("Dev=%X-->%d\n",inputString[3],inputString[3]);
            Serial.printf("Len=%X-->%d\n",inputString[4],inputString[4]);
            Serial.printf("Cmd=%X-->%d\n",inputString[5],inputString[5]);
            Serial.printf("CmdAux=%X-->%d\n",inputString[6],inputString[6]);
            Serial.printf("Status=%X-->%d\n",inputString[7],inputString[7]);
            Serial.printf("PwrFwd=%2X%2X-->%d\n",inputString[8],inputString[9],PwrFwd);
            Serial.printf("PwrRfl=%X%X-->%d\n",inputString[10],inputString[11],PwrRfl);
            Serial.printf("Temp=%X-->%d\n",inputString[12],Temp);
            Serial.printf("Vadj=%2X%2X-->%d\n",inputString[13],inputString[14],Vadj);
            Serial.printf("Vdrv=%2X%2X-->%d\n",inputString[15],inputString[16],Vdrv);
            Serial.printf("Vamp=%2X%2X-->%d\n",inputString[17],inputString[18],Vamp);
            Serial.printf("Idrv=%2X%2X-->%d\n",inputString[19],inputString[20],Idrv);
            Serial.printf("I1=%X%X-->%d\n",inputString[21],inputString[22],I1);
            Serial.printf("I2=%X%X-->%d\n",inputString[23],inputString[24],I2);
            Serial.printf("I3=%X%X-->%d\n",inputString[25],inputString[26],I3);
            Serial.printf("InLevel=%X%X-->%d\n",inputString[27],inputString[28],InLevel);
            Serial.printf("Status1=%X%X-->%d\n",inputString[29],inputString[30],Status1);
            Serial.println(Status1,BIN);//二进制显示Status1的值
            Serial.printf("Status2=%X%X-->%d\n",inputString[31],inputString[32],Status2);
            Serial.printf("Crc=%X-%X-%X-%X\n",inputString[33],inputString[34],inputString[35],inputString[36]);
            Serial.printf("Tail=%X\n",inputString[37]);
 //以下是还原的数据打包成json格式用于mqtt函数发布:           
            //Data = PwrFwd + PwrRfl + Temp + Vadj + Vdrv + Vamp + Idrv + I1 + I2 + I3 + InLevel + Status1 + Status2
            sprintf(msg_buf,"{\"PwrFwd\":%d,\"PwrRfl\":%d,\"Temp\":%d,\"Vadj\":%d,\"Vdrv\":%d,\"Vamp\":%d,\"\Idrv\":%d,\"I1\":%d,\"I2\":%d,\"I3\":%d,\"InLevel\":%d,\"Status1\":%d,\"Status2\":%d,\"Wifi_Rssi\":%d}",
                                PwrFwd,PwrRfl,Temp,Vadj,Vamp,Vdrv,Idrv,I1,I2,I3,InLevel,Status1,Status2,WiFi.RSSI());//构建/pub/8266报文
            Serial.printf("\n");
 //发布主题为"/pub/8266"的消息数据          
         CONFIG buf;
          strcpy(buf.topic, "/pub/8266");
          //Serial.println(msg_buf);//msg_buf代表下位机运行参数的json格式，在串口事件函数里已经还原。
          strcpy(buf.message,msg_buf);
          client.publish(buf.topic, buf.message, true); //数据发送至服务器
          //Serial.print("publish_OK");
          Serial.print("\r\n");     
        
            
            
          
//      memset(&work_data,0,sizeof(work_data));//清空结构体work_data
//      memcpy(&work_data,&inputString,inputString.length());//将串口接收到的数据串复制给结构体work_data，一共有38个byte长度，包扩25字节的运行参数，帧头帧尾校验及其他12字节)
    
  /*         
            Serial.printf("Head_h:%d\n",work_data.Head_h);
            Serial.print(work_data.Head_h,HEX); //用HEX格式显示串口读取到的work_data.Head_h数据
              Serial.print("\r\n");
            Serial.printf("Head_l:%d\n",work_data.Head_l);
            Serial.print(work_data.Head_l,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("Addr:%d\n",work_data.Addr);
            Serial.print(work_data.Addr,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("Dev :%d\n",work_data.Dev);
            Serial.print(work_data.Dev,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
             Serial.print("\r\n");
            Serial.printf("Len:%d\n",work_data.Len);
            Serial.print(work_data.Len,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("Cmd :%d\n",work_data.Cmd);
            Serial.print(work_data.Cmd,HEX); //用HEX格式显示串口读取到的work_data.Cmd数据     
              Serial.print("\r\n");               
            Serial.printf("CmdAux :%d\n",work_data.CmdAux);
            Serial.print(work_data.CmdAux,HEX); //用HEX格式显示串口读取到的work_data.Crc数据     
              Serial.print("\r\n");
             Serial.printf("Status :%d\n",work_data.Status);
            Serial.print(work_data.Status,HEX); //用HEX格式显示串口读取到的work_data.Crc数据     
              Serial.print("\r\n");           
             Serial.printf("PwrFwd :%d\n",work_data.PwrFwd);
            Serial.print(work_data.PwrFwd,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("PwrRfl :%d\n",work_data.PwrRfl);
            Serial.print(work_data.PwrRfl,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("Vadj :%d\n",work_data.Vadj);
            Serial.print(work_data.Vadj,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("Vdrv :%d\n",work_data.Vdrv);
            Serial.print(work_data.Vdrv,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("Vamp :%d\n",work_data.Vamp);
            Serial.print(work_data.Vamp,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("Idrv :%d\n",work_data.Idrv);
            Serial.print(work_data.Idrv,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("I1 :%d\n",work_data.I1);
            Serial.print(work_data.I1,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
             Serial.print("\r\n");
            Serial.printf("I2 :%d\n",work_data.I2);
            Serial.print(work_data.I2,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("I3 :%d\n",work_data.I3);
            Serial.print(work_data.I3,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("InLevel :%d\n",work_data.InLevel);
            Serial.print(work_data.InLevel,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
             Serial.print("\r\n");
            Serial.printf("Status1 :%d\n",work_data.Status1);
            Serial.print(work_data.Status1,HEX); //用HEX格式显示串口读取到的work_data.Status2数据
              Serial.print("\r\n");
            Serial.printf("Status2 :%d\n",work_data.Status2);
            Serial.print(work_data.Status2,HEX); //用HEX格式显示串口读取到的work_data.Status2数据     
              Serial.print("\r\n");
            Serial.printf("Crc :%d\n",work_data.Crc);
            Serial.print(work_data.Crc,HEX); //用HEX格式显示串口读取到的work_data.Crc数据     
              Serial.print("\r\n");              
            Serial.printf("Tail :%d\n",work_data.Tail);
            Serial.print(work_data.Tail,HEX); //用HEX格式显示串口读取到的work_data.Status2数据     
              Serial.print("\r\n");
             */


          }  
 /*********************************智能配网**************************************/
void smartConfig()
{
  Serial.println("SmartConfig begin");
  WiFi.mode(WIFI_STA);
  WiFi.beginSmartConfig();
  int try_time = 0;
  flip_flag = 3 ; //定时器标志位为3
  flipper.attach(0.1, flip); //快闪
  while (1)
  {
    try_time++;
    delay(500);
    if (try_time > 100) {
      try_time = 0;
      ESP.restart();
    }
    //Serial.print(".");
    flipper.attach(0.1, flip);
    if (WiFi.smartConfigDone())
    {
      EEPROM.begin(512);
      CONFIG buf;
      Serial.println("SmartConfig Success");
      flip_flag = 0; //配网成功 状态恢复
      flipper.attach(1, flip); //工作状态
      Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
      Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
      strcpy(buf.ssid,  WiFi.SSID().c_str());
      strcpy(buf.password, WiFi.psk().c_str());
      EEPROM.put<CONFIG>(0, buf);
      EEPROM.commit();
      break;
    }
  }
}
/*********************************MQTT任务**************************************/
void Mqtt_Task()
{  
  if (!client.connected()) {  //服务器掉线重连
       CONFIG buf;
          strcpy(buf.server, "broker-cn.emqx.io");//服务器域名192.168.3.2
          //strcpy(buf.server, "192.168.3.2");//服务器域名192.168.0.136
          port = 1883;

          client.setServer(buf.server, port);
          client.setCallback(callback);
          delay(100);
          Serial.println(port);
          Serial.println(buf.server);
          Serial.println("setServer_OK");     
      reconnect();
    }
    client.loop();
   
  

}
